#include <ani.h>
#include <array>
#include <iostream>
#include <vector>


std::string ANIUtils_ANIStringToStdString(ani_env *env,ani_string ani_str){
    ani_size  strSize;
    env->String_GetUTF8Size(ani_str, &strSize);
   
    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char* utf8_buffer = buffer.data();

    //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written);
    
    utf8_buffer[bytes_written] = '\0'; 
    std::string content = std::string(utf8_buffer); 
    return content;
}

static void handleData_union(ani_env *env, ani_object obj, ani_object union_obj){
    ani_class stringClass;
    env->FindClass("std.core.String", &stringClass);
    
    ani_class arrayBufferClass;
    env->FindClass("escompat.ArrayBuffer", &arrayBufferClass);

    ani_boolean isString;
    env->Object_InstanceOf(union_obj, stringClass, &isString);
    if(isString){
        auto stringContent = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(union_obj));
        std::cout << "Object is String Object Content:" << stringContent.c_str() << std::endl;
        return;
    }

    ani_boolean isArrayBuffer;
    env->Object_InstanceOf(union_obj, arrayBufferClass, &isArrayBuffer);
    if(isArrayBuffer){
        ani_int length;
        env->Object_CallMethodByName_Int(union_obj, "getByteLength", nullptr, &length);
        std::cout << "Object is ArraryBuffer Lenght:" << length << std::endl;
        return; 
    }
    std::cout << "Object is Other Class"<< std::endl;
	return;
}

ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;

    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    static const char *className = "ani_union.ETSGLOBAL";
   
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"handleData", "C{std.core.Object}:", reinterpret_cast<void *>(handleData_union)},
    };

    std::cout << "Start bind native methods to '" << className << "'" << std::endl;

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "Cannot bind native methods to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods to '" << className << "'" << std::endl;

    *result = ANI_VERSION_1;
    return ANI_OK;
}